package czh.cocoa.foundation;

import java.lang.reflect.Method;
import java.util.Formatter;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.DisplayMetrics;
import czh.cocoa.graphics.CGPoint;
import czh.cocoa.graphics.CGRect;
import czh.cocoa.graphics.CGSize;
import czh.cocoa.ui.UIApplication;
import czh.cocoa.ui.UIEdgeInsets;

import android.os.*;
import android.util.*;

public class NSObject {
	
	public NSObject() {
		init();
	}
	
	protected void init() {
		
	}
	
	public boolean isEqual(NSObject o) {
		return this == o;
	}
	
	public int hash() {
		return super.hashCode();
	}
	
	public boolean isKindOfClass(Class<?> clazz) {
		return clazz.isInstance(this);   
	}
	
	public boolean isMemberOfClass(Class<?> clazz) {
		return getClass() == clazz; 
	}
	
	public NSString description() {
		return NSTR(super.toString());
	}
	
	public String toString() { 
		return description().toString();
	}
	
	public boolean equals(Object o) {
		if(o instanceof NSObject) {
			NSObject other = (NSObject)o;
			return isEqual(other);
		}
		return false;
	}
	
	public int hashCode() {
		return hash();
	}
	
	public void performSelector(NSString selector) {
		performSelectorWithObject(selector, null);
	}
	
	public void performSelectorWithObject(NSString selector, NSObject arg) {
		performSelectorWithObjectAfterDelay(selector, arg, -1);
	}
	
	public void performSelectorWithObjectAfterDelay(NSString selector, NSObject arg, double delay) {
		Handler handler = delay >= 0 ? new Handler() : null;
		performSelectorWithObjectAfterDelayWaitUntilDone(handler, selector, arg, delay, false); 
	}
	
	public void performSelectorOnMainThreadWithObjectWaitUntilDone(NSString selector, NSObject arg, boolean wait) {
		Handler handler = new Handler(Looper.myLooper());
		performSelectorWithObjectAfterDelayWaitUntilDone(handler, selector, arg, 0, wait); 
	}
	
	public void performSelectorInBackgroundWithObject(NSString selector, NSObject arg) {
		final NSString selector2 = selector;
		final NSObject arg2 = arg;
		new Thread() {
			public void run() {
				performSelectorWithObject(selector2, arg2);
			}
		}.start();
	}
	
	private void performSelectorWithObjectAfterDelayWaitUntilDone(Handler handler, 
			NSString selector, NSObject arg, double delay, boolean wait) {
		
		final Method method = getMethodForSelector(selector);
		if (method != null) {
			method.setAccessible(true);
			final Class<?>[] paramTypes = method.getParameterTypes(); 
			
			if (paramTypes.length < 2) { 
				final NSObject arg2 = arg;
				final NSObject target = this;
				final boolean wait2 = wait;
				
				final Invocation in = new Invocation();
				in.target = target;
				in.selector = selector;
				in.arg = arg;
				
				final long delays = (long) (delay * 1000);
				
				Runnable callback = new Runnable() {
					@Override
					public void run() {
						
						try {
							if (wait2 && delays > 0) { 
								Thread.sleep(delays);
							}
							
							if (paramTypes.length == 1) { 
								method.invoke(target, arg2);
							} else {
								method.invoke(target);
							}
						} catch (Exception e) {
							e.printStackTrace();
						}
						sInvocations.removeObject(in);
						
					}
				};
				
				in.handler = handler;
				in.callback = callback;
				
				sInvocations.addObject(in);
				
				if (wait) {
					if (handler.getLooper().getThread() == Thread.currentThread()) { 
						callback.run();
					} else {
						handler.post(callback);
					}
				} else if(delay < 0) {
					callback.run();
				} else if (delay == 0) {
					handler.post(callback); 
				} else {
					handler.postDelayed(callback, delays);
				}
				
				return;
			}
			
		}
		
		throw new RuntimeException("object can't response this selector: " + selector);
	}
	
	//- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
	
	private Method getMethodForSelector(NSString selector) {
		Method[] methods = getClass().getDeclaredMethods();
		String name = selector.toString();
		for (Method method : methods) {
			if (method.getName().equals(name)) { 
				return method;
			}
		}
		return null;
	} 
	
//static calls	
	
	private static NSMutableArray sInvocations = new NSMutableArray(3);
	
	public static void cancelPreviousPerformRequestsWithTarget(NSObject target) {
		NSArray invocations = new NSArray(sInvocations);
		for (NSObject o : invocations) {
			Invocation in = (Invocation) o;
			if (in.target == target) { 
				in.handler.removeCallbacks(in.callback);
				sInvocations.removeObject(in);
			}
		}
	}
	
	public static void cancelPreviousPerformRequestsWithTarget(NSObject target, NSString selector, NSObject arg) {
		Invocation in0 = new Invocation();
		in0.target = target;
		in0.selector = selector;
		in0.arg = arg;
		
		NSArray invocations = new NSArray(sInvocations);
		
		for (NSObject o : invocations) {
			Invocation in = (Invocation) o;
			if (in0.isEqual(in)) { 
				in.handler.removeCallbacks(in.callback);
				sInvocations.removeObject(o);
			}
		}
	}
	
	private static class Invocation extends NSObject {
		
		public NSObject target;
		
		public NSString selector;
		
		public NSObject arg;
		
		public Handler handler;
		
		public Runnable callback;
		
		public boolean isEqual(NSObject o) {
			if (o instanceof Invocation) { 
				Invocation other = (Invocation) o;
				if (other.target == target && other.selector.isEqual(selector)) { 
					if (other.arg == null && arg == null || other.arg != null && other.arg.isEqual(arg)) {  
						return true;
					}
				}
			}
			return false;
		}
		
	}
	

//static calls
	
	public final static int NSNotFound = Integer.MAX_VALUE;
	
	public final static int NSNotDefined = Integer.MAX_VALUE;
	
	public final static float HUGE_VALF = Float.MAX_VALUE;
	
	public final static int INT32_MAX = Integer.MAX_VALUE;
	
	public final static float M_PI = (float) Math.PI;
	
	public final static float M_PI_2 = (float) (Math.PI / 2);
	
	public final static float M_PI_4 = (float) (Math.PI / 4);
	
	private static float density = 0;
	
	public static NSString NSTR(String str) {
		return new NSString(str);
	}
	
	public static void NSLog(String format, Object... args) {
		Formatter formatter = new Formatter();
		String info = formatter.format(format, args).toString();
		System.out.println(info);
	}
	
	public static CGPoint CGPointMake(float x, float y) {
		return new CGPoint(x, y);
	}
	
	public static CGSize CGSizeMake(float width, float height) {
		return new CGSize(width, height);
	}
	
	public static CGRect CGRectMake(float x, float y, float width, float height) {
		return new CGRect(x, y, width, height);
	}
	
	public static CGRect CGRectOffset(CGRect rect, float tx, float ty) {
		CGRect rect2 = new CGRect(rect);
		rect2.origin.x += tx;
		rect2.origin.y += ty;
		return rect2;
	}
	
	//additional
	public static CGRect CGRectChange(CGRect rect, float dx, float dy, float dw, float dh) {
		CGRect rect2 = new CGRect(rect);
		rect2.origin.x += dx;
		rect2.origin.y += dy;
		rect2.size.width += dw;
		rect2.size.height += dh;
		return rect2;
	}
	
	public static UIEdgeInsets UIEdgeInsetsMake(float top, float left, float bottom, float right) {
		return new UIEdgeInsets(top, left, bottom, right); 
	}
	
	public static NSRange NSMakeRange(int location, int length) {
		return new NSRange(location, length);
	}
	
	public static float DIP(float dip) {
		if (density == 0) {
			Context ctx = UIApplication.sharedApplication().getContext();
			DisplayMetrics display = ctx.getResources().getDisplayMetrics();
			density = display.density;
		}
		return dip * (density / 2);
	}
	
	public static CGSize DIP(CGSize size) {
		return CGSizeMake(DIP(size.width), DIP(size.height)); 
	}
	
}
